//
//  ViewController.m
//  KVO_Demo
//
//  Created by meitianhui2 on 2017/12/20.
//  Copyright © 2017年 DeLongYang. All rights reserved.
/*
    参考博客： http://tech.glowing.com/cn/implement-kvo/
             http://www.cocoachina.com/ios/20150313/11321.html
    1.0 实验目的一 ： 使用KVC 能否获取私有变量？我们知道使用Runtime 是能获取到的
    2.0 实验目的二 ： 探究KVO 的实现原理 已经搞完
    3.0 实验目的三 ： 手动实现KVO 参考博客： http://tech.glowing.com/cn/implement-kvo/
        https://www.mikeash.com/pyblog/friday-qa-2010-11-6-creating-classes-at-runtime-in-objective-c.html
    4.0 这个VC 没法测试是否 会dealloc 因为是RootVC
 */

#import "ViewController.h"
#import "KVCObject.h"
#import "KVCSubObject.h"
#import "A.h"
#import "Employee.h"
#import "Manager.h"
#import "NSObject+KVO.h"    // 自定义的KVO 仿照系统的实现

@interface Message:NSObject

@property (nonatomic,copy)NSString *text;

@end

@implementation Message

@end




@interface ViewController ()

@property (nonatomic,strong)A *a;

/**
 transactions
 */
@property (nonatomic,strong)NSArray *transactions;

@property (weak, nonatomic) IBOutlet UITextField *textField;

@property (weak, nonatomic) IBOutlet UIButton *customKVOButton;


/**
 Message
 */
@property (strong,nonatomic) Message *message;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
//    [self testKVCGetPrivateProperty];  // 测试获取 私有变量的属性
//    [self testKVCGetPrivatePropertyTwo];  //
    [self testKVCGetMutilayerProperty];
//    [self testKVOPrinciple];
    [self registCustomKVO];
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)dealloc
{
    // 移除KVO 
    [self.a removeObserver:self forKeyPath:@"age"];
    //
    
}

#pragma mark ---- 测试KVC 能否获取私有变量
- (void)testKVCGetPrivateProperty
{
    // 我们测试了 可以获取私有变量
    KVCObject *kvcObj = [[KVCObject alloc] init];
    NSString *name = [kvcObj valueForKeyPath:@"name"];
    NSString *privateObj = [kvcObj valueForKeyPath:@"privatePro"];
    int number = [[kvcObj valueForKeyPath:@"number"] intValue];
    NSLog(@"name is:%@ --- privateObj is:%@ number is:%d",name,privateObj,number);
    [kvcObj setValue:@"Hello" forKeyPath:@"privatePro"];
    // 通过KVC 我们也可以 设置私有的变量的属性
    NSLog(@"new privateObj is %@",[kvcObj valueForKeyPath:@"privatePro"]);
}

- (void)testKVCGetPrivatePropertyTwo
{
    
    KVCSubObject *kvcObj = [[KVCSubObject alloc] init];
    NSString *name = [kvcObj valueForKeyPath:@"name"];
    NSString *privateObj = [kvcObj valueForKeyPath:@"privatePro"];
    int number = [[kvcObj valueForKeyPath:@"number"] intValue];
    kvcObj.kvcSubObjProperty = @"newProperty";
    
    NSString *pro = [kvcObj valueForKeyPath:@"kvcSubObjProperty"];
    NSLog(@"name is:%@ --- privateObj is:%@ number is:%d subObjectPro is:%@",name,privateObj,number,pro);
}


/**
 通过 KVC 获取多层次的属性
 对于 多个 层次的属性等 从employee1 ->manager ->employe2 ->name
 */
- (void)testKVCGetMutilayerProperty
{
    Manager *manager = [[Manager alloc] init];
    manager.arrProperty = @[@"this",@"is",@"a",@"new",@"day"];
    
    Employee *employee1 = [[Employee alloc] init];
    employee1.title = @"iOS developer";
    employee1.name = @"De Long";
    employee1.manager = manager;
    
    Employee *employee2 = [[Employee alloc] init];
    employee2.title = @"Java developer";
    employee2.name = @"Yang";
    employee2.manager = manager;
    
    Employee *employee3 = [[Employee alloc] init];
    employee3.title = @"Web developer";
    employee3.name = @"Ted";
    employee3.manager = manager;
    
    manager.employee1 = employee1;
    manager.employee2 = employee2;
    manager.employee3 = employee3;
    
    // 我们测试一下 多层属性的获取
    NSString *employ2Name = [employee1 valueForKeyPath:@"manager.employee2.name"];
    NSLog(@"employee2 name is %@",employ2Name);
    
    // KVC还提供了集合操作的方法，直接获取到集合属性的同时还能对其进行求和，取平均数，求最大最小值等操作，如下为求和操作，具体可以到苹果官方文档详细了解。
    // 这里造成了crash
//    NSNumber *arrNumber = [manager valueForKeyPath:@"arrProperty.sum"];
//    NSLog(@"arrNumber is %@",arrNumber);
    
    // 也闪退
//    self.transactions = @[@1,@2,@3];
//    NSNumber *amountSum = [self.transactions valueForKeyPath:@"@sum"];
    
    
    // employee2 name is Yang
//    2017-12-22 15:38:42.498662+0800 KVO_Demo[22939:2776565] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSCFConstantString 0x10e88b1c8> valueForUndefinedKey:]: this class is not key value coding-compliant for the key amount.'
    
}

#pragma mark ---- 系统KVO 原理测试
- (void)testKVOPrinciple
{
    //
    self.a = [[A alloc] init];
    [self.a addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew||NSKeyValueChangeOldKey context:nil];
//
}



#pragma mark ---- 触发系统KVO
- (IBAction)changeAProperty:(id)sender
{
    self.a.age = self.a.age+1;
}


- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"age"])
    {
         //  系统不会崩溃会显示这个提示表示 不会自动触发KVO 因为我们自己新建了 NSKVONotifying_A 类 和系统的冲突了 
        //  [general] KVO failed to allocate class pair for name NSKVONotifying_A, automatic key-value observing will not work for this class
        int age = [[change valueForKey:@"age"] intValue];
        NSLog(@"age is %d",age);
    }
}

#pragma mark ---- 自定义 KVO

/**
 自定义的KVO
 */
- (void)registCustomKVO
{
    self.message = [[Message alloc] init];
    NSString *key = NSStringFromSelector(@selector(text));
    [self.message PG_addObserber:self forKey:key withBlock:^(id observingObject, NSString *observedKey, id oldValue, id newValue) {
        NSLog(@"%@ . %@ is now:%@",observingObject,observedKey,newValue);
        dispatch_async(dispatch_get_main_queue(), ^{
            self.textField.text = newValue;
        });
    }];
    
    [self customKVOClick:nil];
}

// 触发KVO
- (IBAction)customKVOClick:(id)sender
{
    NSArray *msgs = @[@"Hello World!", @"Objective C", @"Swift", @"De Long", @"Yang", @"www.apple.com", @"www.google.com"];
    NSUInteger index = arc4random_uniform((u_int32_t)msgs.count);
    self.message.text = msgs[index];
}



- (IBAction)textField:(id)sender
{
}


@end
